<script lang="ts" setup>
import {
  defineProps,
  defineEmits,
  PropType,
  watch,
  ref,
  computed,
  onMounted,
  useSlots,
  defineExpose,
  provide,
  getCurrentInstance,
  toRaw,
} from "vue";
import type { VxeGridProps, VxeGridPropTypes, VxeGridInstance } from "vxe-table";
import { YTableCellRender } from './cell'
import { cloneDeep, debounce } from 'lodash-unified'
import Sortable from 'sortablejs'
import elementResizeDetectorMaker from "element-resize-detector";

import YTableOpearsComp from './components/y-table-operas.vue'
import YTableEditOpearsComp from './components/y-table-edit-operas.vue'
import YTablefilters from './components/y-table-filters.vue'

import { useTableData } from "./hooks/useTableData";
import { usePage } from './hooks/usePage'
import { useCheckRange } from './hooks/useCheckRange'
import { useRowDrag } from './hooks/useDrag'

import { useScopeEvent } from '/@/hooks/useScopeEvent'
import { useKeyboard } from '/@/hooks/useKeyboard'
import { useTableNotCheck } from "/@/hooks/useMessage";
import { IconifyIconOffline } from "../../ReIcon";

const props = defineProps({
  height: { type: [String, Number] as PropType<string | number> },
  data: { type: Array as PropType<any[]>, default: () => [] },
  columns: { type: Array as PropType<YTableColumn[]>, required: true },
  remoteConfig: { type: Object as PropType<YTableRemoteConfig | boolean> },
  pageConfig: { type: Object as PropType<YTablePageConfig | boolean> },
  operas: { type: Array as PropType<YTableOperaOption[]> },
  editOperas: { type: [Object, Array] as PropType<YTableOperas> },
  editOperasFilter: { type: Object },
  filters: { type: Array as PropType<YTableFilter[]> },
  config: { type: Object as PropType<YTableConfig> },
});

const emitHandler = defineEmits(['row-dbl-click', 'cell-click'])

const currInstance = getCurrentInstance()
const getProps = computed(() => ({ ...props } as YTableProps));
const loading = ref(false)

const slots = useSlots()
const socpeEvents = useScopeEvent()
const keyboardState = useKeyboard();

const gridHeight = ref('auto')

const erd = elementResizeDetectorMaker()
const headerRef = ref<HTMLDivElement>()
const footerRef = ref<HTMLDivElement>()
const containerRef = ref<HTMLDivElement>()

const resetContainerHeight = () => {
  containerRef.value.style.height = `calc(100% - ${headerRef.value.offsetHeight + footerRef.value.offsetHeight}px)`

  gridHeight.value = containerRef.value.clientHeight + 'px'
}
const resetContainerHeightDebounce = debounce(resetContainerHeight, 100)
onMounted(() => {
  erd.listenTo(headerRef.value, () => resetContainerHeightDebounce())
  erd.listenTo(footerRef.value, () => resetContainerHeightDebounce())
  resetContainerHeightDebounce()
})


const vxeGridProps = ref({
  headerCellClassName: "y-table-header-cell",
  headerRowClassName: "y-table-header-row",
  round: false,
  rowConfig: {
    keyField: '_id',
    useKey: true,
    isCurrent: true,
    isHover: true
  },
  checkboxConfig: {
    reserve: true,
    highlight: true,
    range: true
  },
  editConfig: {
    trigger: 'click',
    enabled: getProps.value.config?.edit,
    mode: 'cell',
    beforeEditMethod: () => true
  },
  mouseConfig: {
    selected: true
  },
  keyboardConfig: {
    isArrow: true, isDel: true, isEnter: true, isTab: true, isEdit: true
  },
  data: getProps.value.data
} as VxeGridProps);

const vxeGridRef = ref<VxeGridInstance>()

watch(() => getProps.value.data, () => {
  setVxeTable(getProps.value.data)
})

// 列
const globalEdit = computed(() => getProps.value.config?.edit)
const predefineColumns: YTableColumn[] = [
  {
    label: '拖动',
    field: '__drag__',
    width: 50,
    align: 'center',
    visible: false,
    className: 'y-table-cell__drag',
    formatter: {
      key: 'IconifyIconOffline',
      props: {
        icon: 'ep-rank',
        style: {
          cursor: 'move'
        }
      }
    }
  }
]
watch(getProps.value.columns, (columns) => {

  const vxeColumns = predefineColumns.concat(columns).map(column => {
    const renderProps = { columnRaw: column, gridProps: toRaw(getProps.value) }

    const otherOptions = {
      visible: typeof column.visible === 'function' ? column?.visible() : column.visible
    }

    if (globalEdit.value && column.edit) {
      otherOptions['editRender'] = {
        name: YTableCellRender.name,
        props: renderProps,
        enabled: true
      }
    } else {
      otherOptions['cellRender'] = {
        name: YTableCellRender.name,
        props: renderProps
      }
    }

    return {
      colId: column.field,
      field: column.field,
      title: column.label,
      sortable: column.sort,
      width: column.width,
      align: column.align,
      fixed: column.fixed,
      showOverflow: column.showOverflow,
      className: (params: any) => {
        return [
          column.className,
          globalEdit.value && column.edit ? 'y-cell-edit' : ''
        ].join(' ');
      },
      ...otherOptions
    };
  }) as VxeGridPropTypes.Columns;

  vxeColumns.unshift({ type: 'checkbox', visible: false })

  // seq
  if (getProps.value.config?.index !== false) {
    vxeColumns.unshift({ type: 'seq', width: 50 })
  }

  vxeGridProps.value.columns = vxeColumns

}, { immediate: true, deep: true })

// 分页
const { pagerProps, isPagination, loadedCallback } = usePage(getProps)

// 数据加载
const { remoteLoadData, isRemoteData } = useTableData(getProps, vxeGridProps);

const setVxeTable = (data: any[]) => {
  vxeGridProps.value.data = data
  if (vxeGridRef.value) {
    vxeGridRef.value.loadData(data)
  }

}

/* 加载数据 */
const loadData = async (localData: any[] = []) => {
  if (!isRemoteData.value) {
    setVxeTable(localData)
    return;
  }

  loading.value = true
  try {
    const requestParams = { ...cloneDeep(props.remoteConfig?.params) }
    socpeEvents.emit('loadDataBefore', requestParams)

    const { data } = await remoteLoadData(requestParams)

    setVxeTable(data.items)
    loadedCallback(data)
  } catch (err) {
    console.error(err)
  } finally {
    loading.value = false
  }
};
const loadDataDebounce = debounce(loadData, 200)

socpeEvents.on('loadData', loadDataDebounce)
socpeEvents.on('opera-click', (trigger: string) => {
  emitHandler.call(currInstance, 'opera-' + trigger)
})

socpeEvents.on('add-row', row => {
  getProps.value.data.push(row)
  setVxeTable(getProps.value.data)
})

socpeEvents.on('delete-row', rows => {
  const data = getProps.value.data
  for (let row of rows) {
    data.splice(data.findIndex(r => r === row), 1)
  }
  setVxeTable(data)
})

socpeEvents.on('fill-column', ({ field, value, rowIndex }) => {
  const data = getProps.value.data
  data.forEach((row, i) => {
    if (i > rowIndex) {
      row[field] = value
    }
  })

  setVxeTable(data)
})
let rowDrag = false
let rowDragInit = false
socpeEvents.on('row-drag', () => {
  rowDrag = !rowDrag
  if (rowDrag) {
    vxeGridRef.value.showColumn('__drag__')
    if (!rowDragInit) {
      Sortable.create(vxeGridRef.value.$el.querySelector('.body--wrapper>.vxe-table--body tbody'), {
        handle: '.y-table-cell__drag',
        animation: 150,
        onEnd: (sortableEvent) => {
          const data = getProps.value.data
          const newIndex = sortableEvent.newIndex as number
          const oldIndex = sortableEvent.oldIndex as number
          const currRow = data.splice(oldIndex, 1)[0]
          data.splice(newIndex, 0, currRow)
          setVxeTable(data)
        }
      })
      rowDragInit = true
    }
  } else {
    vxeGridRef.value.hideColumn('__drag__')
  }
})

/* 单元格点击 */
const handCellClick = (params) => {

  /* 多选行 */
  useCheckRange({ params, vxeGridProps, vxeGridRef, getProps })

  emitHandler('cell-click', params)
}

const handClickDblClick = (parmas) => {
  emitHandler('row-dbl-click', parmas)
}

onMounted(() => {

  // 默认执行
  if (getProps.value.remoteConfig?.immediate !== false) {
    loadData()
  } else {
    vxeGridRef.value.updateData()
  }
})

provide('tableScopeEvents', socpeEvents)
provide('tableRoot', currInstance)
provide('tableEmit', emitHandler)
provide('tableProps', getProps.value)


const instalceMethods = {
  loadData,
  getData: () => {
    return vxeGridRef.value.getTableData().fullData
  },
  getCheckRecord: (thrwoIfNull: boolean) => {
    return new Promise(reslove => {
      const select = vxeGridRef.value.getCheckboxRecords().find((_, i) => i === 0)
      if (thrwoIfNull && !select) {
        return useTableNotCheck()
      } else {
        reslove(toRaw(select))
      }
    })
  },
  getCheckRecords: (thrwoIfNull: boolean) => {
    return new Promise(reslove => {
      const selects = vxeGridRef.value.getCheckboxRecords(true)
      if (thrwoIfNull && !selects.length) {
        return useTableNotCheck()
      } else {
        reslove(selects.map(toRaw))
      }
    })
  },
  addRow: (row: any, rowIndex: any = null) => {
    return instalceMethods.addRows([row], rowIndex)
  },
  addRows: (rows: any[], rowIndex: any = null) => {
    if (rowIndex !== null) {
      return vxeGridRef.value.insertAt(rows, rowIndex)
    }
    return vxeGridRef.value.insertAt(rows, vxeGridRef.value.getData().length - 1)
  },
  updateOperas: () => {
    socpeEvents.emit('update-operas')
  },
  filter: () => {
    return filterRef.value;
  },
  vxe: () => vxeGridRef.value
}

defineExpose(instalceMethods)


const filterRef = ref(null)
</script>

<template>
  <div v-loading="loading" class="y-table" :class="{
    'user-select-none':keyboardState.shift
  }" :style="{
  height: getProps.height === 'auto' ? '100%' : 'none'
  }">
    <div class="y-table__header" ref="headerRef">
      <YTablefilters v-if="getProps.filters?.length || getProps.columns.some(s => s.filter || s.filterComponent)"
        :options="getProps.filters" ref="filterRef" />
    </div>
    <div class="y-table__container" ref="containerRef">
      <VxeGrid v-bind="vxeGridProps" ref="vxeGridRef" :height="gridHeight" @cell-click="handCellClick"
        @cell-dblclick="handClickDblClick">
        <template #toolbar v-if="getProps.operas?.length">
          <div class="y-table__toolbar">
            <div class="y-table__toolbar_left">
              <YTableOpearsComp v-if="getProps.operas?.length" :options="getProps.operas" />
            </div>
            <div class="y-table__toolbar_right">

            </div>
          </div>
        </template>
      </VxeGrid>
    </div>
    <div class="y-table__footer" ref="footerRef">
      <div class="y-table__footer_left">
        <YTableEditOpearsComp v-if="getProps.editOperas" :options="getProps.editOperas" />
        <!-- <slot name="footerLeft"></slot> -->
      </div>
      <el-pagination class="y-table__footer_right" v-if="isPagination" v-bind="pagerProps" />
    </div>
  </div>
</template>
